/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: integrations for semaphore test 
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-4-12      JasonHu           Init
 */

#include <base/mutex.h>
#include <base/thread.h>
#define NX_LOG_NAME "semaphore"
#define NX_LOG_LEVEL NX_LOG_DBG
#include <base/log.h>
#include <base/clock.h>
#include <test/integration.h>
#include <base/clock.h>

#ifdef CONFIG_NX_TEST_INTEGRATION_MUTEX

#define MUTEX_THREAD_NR 10

NX_PRIVATE NX_Mutex mutex;
NX_PRIVATE volatile int state = 0;
NX_PRIVATE volatile int count = 0;

NX_PRIVATE void TestThread(void *arg)
{
    while (1)
    {
        NX_MutexLock(&mutex);
        count++;
        NX_MutexUnlock(&mutex);
        if (count > 1000000)
        {
            break;
        }
    }
    state++;
}


NX_PRIVATE void MutexTest(void)
{
    int i;

    state = 0;
    NX_MutexInit(&mutex);

    for (i = 0; i < MUTEX_THREAD_NR; i++)
    {
        NX_Thread *thread = NX_ThreadCreate("mutex", TestThread, NX_NULL, NX_THREAD_PRIORITY_NORMAL);
        if (!thread)
        {
            NX_LOG_E("create thread error!");
            return;
        }
        NX_ThreadStart(thread);
    }
    
    while (state != MUTEX_THREAD_NR)
    {
        NX_Printf("sleep 1s");
        NX_ThreadSleep(1000); /* sleep 1s */
    }
    NX_Printf("test mutex ok!");
}

#ifdef NX_SCHED_REALTIME
NX_PRIVATE NX_Thread *thread1 = NX_NULL;
NX_PRIVATE NX_Thread *thread2 = NX_NULL;
NX_PRIVATE NX_Thread *thread3 = NX_NULL;

/* test thread priority */
#define THREAD_PRIORITY       (NX_THREAD_PRIORITY_HIGH + 2)

NX_PRIVATE void ThreadHandler1(void *arg)
{
    /* sleep, let lower priority thread run */
    NX_ThreadSleep(100);

    /* now thread3 hold mutex，and thread2 waitting for the mutex */

    /* check thread2 and thread3 priority */
    if (thread2->priority != thread3->priority)
    {
        /* test failed if priority not equal */
        NX_Printf("the priority of thread2 is: %d\n", thread2->priority);
        NX_Printf("the priority of thread3 is: %d\n", thread3->priority);
        NX_Printf("test failed.\n");
    }
    else
    {
        NX_Printf("the priority of thread2 is: %d\n", thread2->priority);
        NX_Printf("the priority of thread3 is: %d\n", thread3->priority);
        NX_Printf("test OK.\n");
    }
}

NX_PRIVATE void ThreadHandler2(void *arg)
{
    NX_Error result;

    NX_Printf("the priority of thread2 is: %d\n", thread2->priority);

    /* sleep, let lower priority thread run */
    NX_ThreadSleep(50);

    /*
     * try get lock, but mutex was lock, raise holder priority
     */
    result = NX_MutexLock(&mutex);

    if (result == NX_EOK)
    {
        NX_MutexUnlock(&mutex);
    }
}

NX_PRIVATE void ThreadHandler3(void *arg)
{
    NX_Error result;

    NX_Printf("the priority of thread3 is: %d\n", thread3->priority);

    result = NX_MutexLock(&mutex);
    if (result != NX_EOK)
    {
        NX_Printf("thread3 take a mutex, failed.\n");
    }

    /* delay for a while, 500ms */
    NX_ClockTickDelayMillisecond(500);

    NX_MutexUnlock(&mutex);
}

NX_PRIVATE void MutexPriorityInversion(void)
{
    NX_MutexInit(&mutex);

    thread1 = NX_ThreadCreate("thread1", ThreadHandler1, NX_NULL, THREAD_PRIORITY + 1);

    if (thread1 != NX_NULL)
    {
        NX_ThreadStart(thread1);
    }

    thread2 = NX_ThreadCreate("thread2",
                            ThreadHandler2,
                            NX_NULL,
                            THREAD_PRIORITY);
    if (thread2 != NX_NULL)
    {
        NX_ThreadStart(thread2);
    }
    thread3 = NX_ThreadCreate("thread3",
                            ThreadHandler3,
                            NX_NULL,
                            THREAD_PRIORITY - 1);
    if (thread3 != NX_NULL)
    {
        NX_ThreadStart(thread3);
    }
}
#endif
NX_INTEGRATION_TEST(NX_Mutex)
{
    MutexTest();
#ifdef NX_SCHED_REALTIME
    MutexPriorityInversion();
#endif
    return NX_EOK;
}

#endif
